package cn.iocoder.yudao.module.system.service.pay;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.pay.vo.*;
import cn.iocoder.yudao.module.system.controller.admin.product.vo.ProductRespVO;
import cn.iocoder.yudao.module.system.controller.admin.productsku.vo.ProductSkuRespVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.order.TenantOrderSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.product.ProductDO;
import cn.iocoder.yudao.module.system.dal.dataobject.product.ProductRenewalItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.productsku.ProductSkuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantOrderDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantOrderReceiveDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantOrderUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantProductDO;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantOrderReceiveMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantOrderUserMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.order.OrderTypeEnum;
import cn.iocoder.yudao.module.system.enums.product.ProductDeliveryStatusEnum;
import cn.iocoder.yudao.module.system.enums.product.ProductDeliveryTypeEnum;
import cn.iocoder.yudao.module.system.enums.product.ProductEnum;
import cn.iocoder.yudao.module.system.service.product.ProductService;
import cn.iocoder.yudao.module.system.service.productsku.ProductSkuService;
import cn.iocoder.yudao.module.system.service.tenant.TenantOrderService;
import cn.iocoder.yudao.module.system.service.tenant.TenantProductService;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 呼叫中心支付 Service 实现类
 *
 * @author 数创万维
 */
@Service
@Validated
@Slf4j
public class SystemPayServiceImpl implements SystemPayService {

    @Resource
    private PayOrderApi payOrderApi;

    @Resource
    private TenantOrderService tenantOrderService;

    @Resource
    private ProductService productService;

    @Resource
    private ProductSkuService productSkuService;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private TenantService tenantService;

    @Resource
    private TenantOrderUserMapper tenantOrderUserMapper;

    @Resource
    private TenantOrderReceiveMapper tenantOrderReceiveMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public SystemPayConfirmRespVO confirmPay(SystemPayConfirmReqVO reqVO) {

        ProductSkuDO productSku = productSkuService.getProductSku(reqVO.getSkuId());
        if (ObjectUtil.isNull(productSku)) {
            throw exception(PRODUCT_SKU_NOT_EXISTS);
        }
        // 自定义数量的
        if (productSku.getType() == 2) {
            if (Objects.isNull(reqVO.getNum()) || reqVO.getNum() < 1) {
                throw exception(NUM_MUST_NOT_ZERO);
            }
            productSku.setPrice(productSku.getPrice().multiply(BigDecimal.valueOf(reqVO.getNum())));
            productSku.setOldPrice(productSku.getPrice());
        }
        ProductDO product = productService.getProduct(productSku.getProductId());
        if (ObjectUtil.isNull(product)) {
            throw exception(PRODUCT_NOT_EXISTS);
        }

        SystemPayConfirmRespVO respVO = new SystemPayConfirmRespVO();

        // 获取产品规格： 价格， 数量，优惠金额，适用范围，返回给前端 -> 生成预支付订单
        String preOrderNo = generateOrderNo();
        respVO.setPreOrderNo(preOrderNo);
        respVO.setProductInfo(BeanUtils.toBean(product, ProductRespVO.class));
        respVO.setSkuInfo(BeanUtils.toBean(productSku, ProductSkuRespVO.class));
        // 如果reqVo 里面没有携带User，那么使用登录用户
        respVO.setOwnerUserId(getLoginUserId());
        respVO.setUserIdList(reqVO.getUserIdList());
        respVO.setNum(productSku.getType() == 1 ? productSku.getNum() : reqVO.getNum());
        respVO.setOrderType(OrderTypeEnum.BUY.getType());
        // 计算金额
        calculatePrice(respVO, 1);

        // 获取钱包余额
        respVO.setWallet(tenantService.getWallet(getLoginUser().getTenantId()));

        fillUsername(reqVO.getUserIdList(), respVO);

        // 补充收货信息
        respVO.setDeliveryType(reqVO.getDeliveryType()).setReceiver(reqVO.getReceiver()).setMobile(reqVO.getMobile()).setAreaId(reqVO.getAreaId()).setDetailAddress(reqVO.getDetailAddress());

        stringRedisTemplate.opsForValue().set(RedisKeyConstants.PREPAY + preOrderNo, JSONUtil.toJsonStr(respVO));
        stringRedisTemplate.expire(RedisKeyConstants.PREPAY + preOrderNo, 1, TimeUnit.HOURS);
        return respVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SystemPayConfirmRespVO confirmRenewalPay(SystemPayRenewalConfirmReqVO reqVO) {

        ProductDO product = productService.getProductByCode(reqVO.getBizType());

        // 获取item
        ProductRenewalItemDO itemDO = productService.getRenewalItem(reqVO.getItemId());

        SystemPayConfirmRespVO respVO = new SystemPayConfirmRespVO();

        // 获取产品规格： 价格， 数量，优惠金额，适用范围，返回给前端 -> 生成预支付订单
        String preOrderNo = generateOrderNo();
        respVO.setPreOrderNo(preOrderNo);
        respVO.setProductInfo(BeanUtils.toBean(product, ProductRespVO.class));
        respVO.setSkuInfo(new ProductSkuRespVO().setId(reqVO.getBizId()).setNum(1).setExpireTime(itemDO.getValue()).setOldPrice(itemDO.getPrice()).setPrice(itemDO.getPrice()));
        // 如果reqVo 里面没有携带User，那么使用登录用户
        respVO.setOwnerUserId(getLoginUserId());
        respVO.setUserIdList(null);
        respVO.setNum(itemDO.getValue());
        respVO.setOrderType(OrderTypeEnum.RENEWAL.getType());
        // 计算金额
        calculatePrice(respVO, reqVO.getNum());

        // 获取钱包余额
        respVO.setWallet(tenantService.getWallet(getLoginUser().getTenantId()));

        fillUsername(null, respVO);

        // 补充收货信息
        respVO.setDeliveryType(ProductDeliveryTypeEnum.SYSTEM.getType());

        stringRedisTemplate.opsForValue().set(RedisKeyConstants.PREPAY + preOrderNo, JSONUtil.toJsonStr(respVO));
        stringRedisTemplate.expire(RedisKeyConstants.PREPAY + preOrderNo, 1, TimeUnit.HOURS);
        return respVO;
    }

    @Override
    public SystemPayConfirmRespVO getPreOrderNo(String preOrderNo) {
        String orderStr = stringRedisTemplate.opsForValue().get(RedisKeyConstants.PREPAY + preOrderNo);
        if (StrUtil.isEmpty(orderStr)) {
            throw exception(ORDER_RECORD_NOT_EXISTS);
        }
        return BeanUtils.toBean(JSONUtil.parse(orderStr), SystemPayConfirmRespVO.class);
    }

    @Override
    public Boolean getOrderIsPaid(String preOrderNo) {
        String confirmOrderId = stringRedisTemplate.opsForValue().get(RedisKeyConstants.CONFIRM_ORDER + preOrderNo);
        if (StrUtil.isEmpty(confirmOrderId)) {
            return false;
        }
        PayOrderRespDTO order = payOrderApi.getOrder(Long.parseLong(confirmOrderId));
        if (Objects.isNull(order)) {
            return false;
        }
        return PayOrderStatusEnum.isSuccess(order.getStatus());
    }

    private void fillUsername(List<Long> userId, SystemPayConfirmRespVO respVO) {
        if (ObjectUtil.isNotNull(userId) && CollUtil.isNotEmpty(userId)) {
            respVO.setUsernameList(adminUserApi.getUserList(userId).stream().map(AdminUserRespDTO::getNickname).collect(Collectors.toList()));
        }
    }

    private void calculatePrice(SystemPayConfirmRespVO respVO, Integer num) {

        ProductSkuRespVO sku = respVO.getSkuInfo();
        int total = (Objects.isNull(respVO.getUserIdList()) || CollUtil.isEmpty(respVO.getUserIdList())) ? num : respVO.getUserIdList().size() * num;

        BigDecimal oldPrice = sku.getOldPrice().multiply(BigDecimal.valueOf(total));

        BigDecimal price = sku.getPrice().multiply(BigDecimal.valueOf(total));

        BigDecimal couponPrice = oldPrice.subtract(price);

        respVO.setTotalPrice(oldPrice);
        respVO.setCouponPrice(couponPrice);
        respVO.setPayPrice(price);
        respVO.setMoreCouponPrice(BigDecimal.ZERO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPay(SystemPayCreateReqVO systemPayCreateReqVO) {

        String preOrderStr = stringRedisTemplate.opsForValue().get(RedisKeyConstants.PREPAY + systemPayCreateReqVO.getPreOrderNo());

        if (ObjectUtil.isEmpty(preOrderStr)) {
            throw exception(ORDER_RECORD_NOT_EXISTS);
        }

        // 防止重复提交
        String confirmOrderId = stringRedisTemplate.opsForValue().get(RedisKeyConstants.CONFIRM_ORDER + systemPayCreateReqVO.getPreOrderNo());
        if (StrUtil.isNotEmpty(confirmOrderId)) {
            return Long.parseLong(confirmOrderId);
        }

        SystemPayConfirmRespVO preOrder = BeanUtils.toBean(JSONUtil.parseObj(preOrderStr), SystemPayConfirmRespVO.class);
        // 生成订单
        TenantOrderDO tenantOrder = new TenantOrderDO();
        tenantOrder
                .setOrderNo(preOrder.getPreOrderNo())
                .setSubject(preOrder.getProductInfo().getName())
                .setPayStatus(PayOrderStatusEnum.WAITING.getStatus())
                .setPayType(systemPayCreateReqVO.getPayType())
                .setBizId(preOrder.getSkuInfo().getId())
                .setBizType(preOrder.getProductInfo().getCode())
                .setTotalPrice(preOrder.getTotalPrice())
                .setCouponPrice(preOrder.getCouponPrice().add(preOrder.getMoreCouponPrice()))
                .setPayPrice(preOrder.getPayPrice())
                .setOwnerUserId(preOrder.getOwnerUserId())
                .setDeliveryType(preOrder.getProductInfo().getDeliveryType())
                .setDeliveryStatus(ProductDeliveryStatusEnum.UN_DELIVERY.getStatus())
                .setNum(preOrder.getNum())
                .setOrderType(preOrder.getOrderType())
        ;
        // 放到redis缓存去 (40分钟有效) : -> 修改为直接入库
        Long orderId = tenantOrderService.createTenantOrder(BeanUtils.toBean(tenantOrder, TenantOrderSaveReqVO.class));

        if (ObjectUtil.isNotNull(preOrder.getUserIdList()) && CollUtil.isNotEmpty(preOrder.getUserIdList())) {
            for (Long id : preOrder.getUserIdList()) {
                tenantOrderUserMapper.insert(new TenantOrderUserDO().setOwnerUserId(id).setOrderId(orderId));
            }
        }
        if (!ProductDeliveryTypeEnum.isSystem(tenantOrder.getDeliveryType())) {
            // 插入订单+收货人信息 ： 需校验是否已经存在对应的 orderId->有的话直接更新， 没有的话插入
            TenantOrderReceiveDO orderReceiveDO = tenantOrderReceiveMapper.selectOne(TenantOrderReceiveDO::getOrderId, orderId);
            if (Objects.isNull(orderReceiveDO)) {
                orderReceiveDO = new TenantOrderReceiveDO();
                orderReceiveDO.setOrderId(orderId).setReceiver(preOrder.getReceiver()).setMobile(preOrder.getMobile()).setAreaId(preOrder.getAreaId()).setDetailAddress(preOrder.getDetailAddress());
                tenantOrderReceiveMapper.insert(orderReceiveDO);
            } else {
                orderReceiveDO.setOrderId(orderId).setReceiver(preOrder.getReceiver()).setMobile(preOrder.getMobile()).setAreaId(preOrder.getAreaId()).setDetailAddress(preOrder.getDetailAddress());
                tenantOrderReceiveMapper.updateById(orderReceiveDO);
            }
        }

        // 判断是否用钱包余额支付？ 当 payType = 1, 直接余额扣款
        if (!Objects.equals(preOrder.getProductInfo().getCode(), ProductEnum.WALLET.getCode()) && systemPayCreateReqVO.getPayType() == 1) {
            // 判断余额是否充足
            Long tenantId = getLoginUser().getTenantId();
            BigDecimal wallet = tenantService.getWallet(tenantId);
            if (wallet.compareTo(tenantOrder.getPayPrice()) >= 0) {
                tenantService.updateWallet(tenantId, tenantOrder.getPayPrice().doubleValue(), false);
                // 执行订单的通知
                tenantOrderService.updateOrderPaid(new PayOrderNotifyReqDTO().setMerchantOrderId(tenantOrder.getOrderNo()).setPayOrderId(0L));
                return -1L;
            } else {
                throw exception(TENANT_WALLET_NOT_ENOUGH);
            }
        }

        @Valid PayOrderCreateReqDTO payOrderCreateReqDTO = new PayOrderCreateReqDTO();
        payOrderCreateReqDTO.setSubject("数创万维CRM");
        Integer price = tenantOrder.getPayPrice().multiply(BigDecimal.TEN).multiply(BigDecimal.TEN).intValue();
        payOrderCreateReqDTO.setPrice(price)
                .setBody(tenantOrder.getSubject())
                .setAppId(systemPayCreateReqVO.getAppId())
                .setUserIp(getClientIP())
                .setMerchantOrderId(tenantOrder.getOrderNo())
                .setExpireTime(DateUtil.offsetMinute(new Date(), 30).toLocalDateTime());

        Long order = payOrderApi.createOrder(payOrderCreateReqDTO);
        stringRedisTemplate.opsForValue().set(RedisKeyConstants.CONFIRM_ORDER + tenantOrder.getOrderNo(), String.valueOf(order), 30, TimeUnit.MINUTES);
        return order;
    }

    @Override
    public SystemPayConfirmRespVO createWalletRecharge(SystemWalletRechargeReqVO reqVO) {

        ProductDO product = productService.getProductByCode(ProductEnum.WALLET.getCode());

        if (ObjectUtil.isNull(product)) {
            throw exception(PRODUCT_NOT_EXISTS);
        }
        SystemPayConfirmRespVO respVO = new SystemPayConfirmRespVO();
        // 获取产品规格： 价格， 数量，优惠金额，适用范围，返回给前端 -> 生成预支付订单
        String preOrderNo = generateOrderNo();
        respVO.setPreOrderNo(preOrderNo);
        respVO.setProductInfo(BeanUtils.toBean(product, ProductRespVO.class));
        respVO.setSkuInfo(new ProductSkuRespVO().setId(0L)
                .setNum(reqVO.getRechargeValue().intValue())
                .setOldPrice(reqVO.getRechargeValue())
                .setPrice(reqVO.getRechargeValue())
                .setExpireTime(0));

        // 如果reqVo 里面没有携带User，那么使用登录用户
        respVO.setOwnerUserId(getLoginUserId());
        respVO.setNum(1);
        respVO.setOrderType(OrderTypeEnum.WALLET.getType());
        // 计算金额
        calculatePrice(respVO, 1);

        // 获取钱包余额
        respVO.setWallet(tenantService.getWallet(TenantContextHolder.getTenantId()));

//        fillUsername(getLoginUserId(), respVO);

        stringRedisTemplate.opsForValue().set(RedisKeyConstants.PREPAY + preOrderNo, JSONUtil.toJsonStr(respVO));
        stringRedisTemplate.expire(RedisKeyConstants.PREPAY + preOrderNo, 1, TimeUnit.HOURS);
        // 生成订单
//        TenantOrderDO tenantOrder = new TenantOrderDO();
//        tenantOrder
//                .setOrderNo(generateOrderNo())
//                .setSubject("钱包充值")
//                .setPayStatus(0)
//                .setPayType(reqVO.getPayType())
//                .setSkuId(0L)
//                .setProductId(productDO.getId())
//                .setTotalPrice(reqVO.getRechargeValue())
//                .setCouponPrice(BigDecimal.ZERO)
//                .setPayPrice(reqVO.getRechargeValue())
//                .setOwnerUserId(0L)
//        ;
//        // 放到redis缓存去 (40分钟有效) : -> 修改为直接入库
//        tenantOrderService.createTenantOrder(BeanUtils.toBean(tenantOrder, TenantOrderSaveReqVO.class));
//        @Valid PayOrderCreateReqDTO payOrderCreateReqDTO = new PayOrderCreateReqDTO();
//        payOrderCreateReqDTO.setSubject("数创万维CRM");
//        Integer price = tenantOrder.getPayPrice().intValue() * 100;
//        payOrderCreateReqDTO.setPrice(price)
//                .setBody(tenantOrder.getSubject())
//                .setAppId(reqVO.getAppId())
//                .setUserIp(getClientIP())
//                .setMerchantOrderId(tenantOrder.getOrderNo())
//                .setExpireTime(DateUtil.offsetMinute(new Date(), 30).toLocalDateTime());

        return respVO;
    }

    private String generateOrderNo() {
        String format = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
        return StrUtil.concat(true, format, StrUtil.fillBefore(RandomUtil.randomNumbers(2), '0', 6));
    }
}